// ErrorCollector.ts - 错误采集器
// @/utils/monitor/collectors/ErrorCollector.ts

import { MonitorEventBus } from '../core/MonitorEventBus';
import { MonitorConfig, EventType, ErrorEventData, ICollector } from '../MonitorTypes';

/**
 * 错误采集器
 * 采集JS错误、Promise拒绝、资源加载错误、Vue错误等
 */
export class ErrorCollector implements ICollector {
  public readonly name = 'ErrorCollector';
  
  private eventBus: MonitorEventBus;
  private config: MonitorConfig;
  private errorCache: Set<string>;
  private isStarted: boolean = false;
  private cleanup: Array<() => void> = [];

  constructor(eventBus: MonitorEventBus, config: MonitorConfig) {
    this.eventBus = eventBus;
    this.config = config;
    this.errorCache = new Set();
  }

  /**
   * 初始化采集器
   */
  public init(config: MonitorConfig): void {
    this.config = config;
  }

  /**
   * 启动错误采集
   */
  public start(): void {
    if (this.isStarted) {
      return;
    }

    this.setupJavaScriptErrorTracking();
    this.setupPromiseRejectionTracking();
    this.setupResourceErrorTracking();
    this.setupVueErrorTracking();
    
    // 设置定期清理过期缓存
    this.setupCacheCleanup();
    
    this.isStarted = true;

    if (this.config.enableDebug) {
      console.log('[ErrorCollector] Started error tracking');
    }
  }

  /**
   * 停止错误采集
   */
  public stop(): void {
    if (!this.isStarted) {
      return;
    }

    // 清理所有事件监听器
    this.cleanup.forEach(fn => fn());
    this.cleanup = [];
    
    this.isStarted = false;

    if (this.config.enableDebug) {
      console.log('[ErrorCollector] Stopped error tracking');
    }
  }

  /**
   * 销毁采集器
   */
  public destroy(): void {
    this.stop();
    this.errorCache.clear();
  }

  /**
   * 设置JavaScript错误追踪
   */
  private setupJavaScriptErrorTracking(): void {
    // #ifdef H5
    if (typeof window !== 'undefined') {
      const errorHandler = (event: ErrorEvent) => {
        this.handleJavaScriptError(event);
      };

      window.addEventListener('error', errorHandler, true);
      
      this.cleanup.push(() => {
        window.removeEventListener('error', errorHandler, true);
      });
    }
    // #endif

    // #ifdef MP
    // 小程序环境的错误处理
    if (typeof uni !== 'undefined') {
      const originalOnError = uni.onError;
      uni.onError = (error: string) => {
        this.handleMiniProgramError(error);
        if (originalOnError) {
          originalOnError(error);
        }
      };

      this.cleanup.push(() => {
        if (originalOnError) {
          uni.onError = originalOnError;
        }
      });
    }
    // #endif
  }

  /**
   * 设置Promise拒绝追踪
   */
  private setupPromiseRejectionTracking(): void {
    // #ifdef H5
    if (typeof window !== 'undefined') {
      const rejectionHandler = (event: PromiseRejectionEvent) => {
        this.handlePromiseRejection(event);
      };

      window.addEventListener('unhandledrejection', rejectionHandler);
      
      this.cleanup.push(() => {
        window.removeEventListener('unhandledrejection', rejectionHandler);
      });
    }
    // #endif
  }

  /**
   * 设置资源错误追踪
   */
  private setupResourceErrorTracking(): void {
    // #ifdef H5
    if (typeof window !== 'undefined') {
      const resourceErrorHandler = (event: Event) => {
        this.handleResourceError(event);
      };

      window.addEventListener('error', resourceErrorHandler, true);
      
      this.cleanup.push(() => {
        window.removeEventListener('error', resourceErrorHandler, true);
      });
    }
    // #endif
  }

  /**
   * 设置Vue错误追踪
   */
  private setupVueErrorTracking(): void {
    // Vue全局错误处理器
    if (typeof window !== 'undefined' && (window as any).Vue) {
      const Vue = (window as any).Vue;
      const originalErrorHandler = Vue.config.errorHandler;

      Vue.config.errorHandler = (err: Error, vm: any, info: string) => {
        this.handleVueError(err, vm, info);
        
        // 调用原始错误处理器
        if (originalErrorHandler) {
          originalErrorHandler(err, vm, info);
        }
      };

      this.cleanup.push(() => {
        Vue.config.errorHandler = originalErrorHandler;
      });
    }
  }

  /**
   * 设置缓存清理
   */
  private setupCacheCleanup(): void {
    const cleanup = setInterval(() => {
      // 可以在这里实现更智能的缓存清理策略
      if (this.errorCache.size > 100) {
        this.errorCache.clear();
        if (this.config.enableDebug) {
          console.log('[ErrorCollector] Cleared error cache due to size limit');
        }
      }
    }, 5 * 60 * 1000); // 5分钟检查一次

    this.cleanup.push(() => {
      clearInterval(cleanup);
    });
  }

  /**
   * 处理JavaScript错误
   */
  private handleJavaScriptError(event: ErrorEvent): void {
    const errorKey = this.getErrorKey(event);
    
    // 避免重复上报相同错误
    if (this.errorCache.has(errorKey)) {
      return;
    }
    
    this.errorCache.add(errorKey);
    
    // 5分钟后从缓存中移除
    setTimeout(() => {
      this.errorCache.delete(errorKey);
    }, 5 * 60 * 1000);

    const errorData: ErrorEventData = {
      message: event.message,
      filename: event.filename,
      lineno: event.lineno,
      colno: event.colno,
      source: 'js',
      stack: event.error?.stack ? this.parseStackTrace(event.error.stack) : undefined
    };

    // 添加额外的错误信息
    if (event.error) {
      errorData.stack = this.parseStackTrace(event.error.stack);
    }

    this.emitError(errorData);
  }

  /**
   * 处理小程序错误
   */
  private handleMiniProgramError(error: string): void {
    const errorData: ErrorEventData = {
      message: error,
      source: 'js',
      filename: 'miniprogram',
      lineno: 0,
      colno: 0
    };

    this.emitError(errorData);
  }

  /**
   * 处理Promise拒绝
   */
  private handlePromiseRejection(event: PromiseRejectionEvent): void {
    const reason = event.reason;
    
    const errorData: ErrorEventData = {
      message: reason?.message || String(reason),
      stack: reason?.stack ? this.parseStackTrace(reason.stack) : undefined,
      source: 'promise',
      filename: 'promise-rejection',
      lineno: 0,
      colno: 0
    };

    this.emitError(errorData);
  }

  /**
   * 处理资源错误
   */
  private handleResourceError(event: Event): void {
    const target = event.target as HTMLElement;
    
    // 检查是否是资源加载错误
    if (target !== window && target?.nodeName) {
      const url = (target as any).src || (target as any).href || '';
      
      const errorData: ErrorEventData = {
        message: `Failed to load ${target.nodeName.toLowerCase()}: ${url}`,
        source: 'resource',
        filename: url,
        lineno: 0,
        colno: 0
      };

      this.emitError(errorData);
    }
  }

  /**
   * 处理Vue错误
   */
  private handleVueError(err: Error, vm: any, info: string): void {
    const errorData: ErrorEventData = {
      message: err.message,
      stack: err.stack ? this.parseStackTrace(err.stack) : undefined,
      source: 'vue',
      filename: 'vue-component',
      lineno: 0,
      colno: 0,
      componentName: vm?.$options?.name || vm?.$options?._componentTag || 'Anonymous',
      propsData: this.sanitizeVueData(vm?.$props)
    };

    // 添加Vue特定信息
    if (info) {
      errorData.message = `${err.message} (${info})`;
    }

    this.emitError(errorData);
  }

  /**
   * 发送错误事件
   */
  private emitError(errorData: ErrorEventData): void {
    this.eventBus.emit(EventType.ERROR, {
      type: EventType.ERROR,
      data: errorData,
      timestamp: Date.now()
    });

    if (this.config.enableDebug) {
      console.log('[ErrorCollector] Error captured:', errorData);
    }
  }

  /**
   * 生成错误缓存键
   */
  private getErrorKey(event: ErrorEvent): string {
    return `${event.message}-${event.filename}-${event.lineno}-${event.colno}`;
  }

  /**
   * 解析堆栈跟踪
   */
  private parseStackTrace(stack?: string): string {
    if (!stack) return '';
    
    try {
      // 简化堆栈信息，只保留前10行并清理敏感信息
      const lines = stack.split('\n').slice(0, 10);
      return lines
        .map(line => line.trim())
        .filter(line => line.length > 0)
        .join('\n');
    } catch (error) {
      return stack;
    }
  }

  /**
   * 清理Vue数据，避免敏感信息
   */
  private sanitizeVueData(data: any): any {
    if (!data || typeof data !== 'object') {
      return data;
    }

    try {
      const sanitized: any = {};
      Object.keys(data).forEach(key => {
        if (key.startsWith('_') || typeof data[key] === 'function') {
          return; // 跳过私有属性和函数
        }
        
        const value = data[key];
        if (typeof value === 'object' && value !== null) {
          sanitized[key] = '[Object]';
        } else if (typeof value === 'string' && value.length > 100) {
          sanitized[key] = value.substring(0, 100) + '...';
        } else {
          sanitized[key] = value;
        }
      });
      
      return sanitized;
    } catch (error) {
      return '[Unable to serialize]';
    }
  }

  /**
   * 获取采集器状态信息
   */
  public getStatus(): { isStarted: boolean; cacheSize: number; name: string } {
    return {
      name: this.name,
      isStarted: this.isStarted,
      cacheSize: this.errorCache.size
    };
  }
} 